package com.example.terminalservice.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.terminalservice.common.*;
import com.example.terminalservice.exception.MyException;
import com.example.terminalservice.mapper.MenuMapper;
import com.example.terminalservice.mapper.RoleMapper;
import com.example.terminalservice.mapper.UserRoleMapper;
import com.example.terminalservice.pojo.Role;
import com.example.terminalservice.pojo.User;
import com.example.terminalservice.pojo.UserRole;
import com.example.terminalservice.pojo.dto.LoginDTO;
import com.example.terminalservice.pojo.dto.PwdDTO;
import com.example.terminalservice.pojo.dto.RegisterDTO;
import com.example.terminalservice.pojo.dto.UserDTO;
import com.example.terminalservice.service.UserService;
import com.example.terminalservice.mapper.UserMapper;
import com.example.terminalservice.utils.DateUtils;
import com.example.terminalservice.utils.FileUploadUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* @author 28142
* @description 针对表【sys_user】的数据库操作Service实现
* @createDate 2023-08-04 16:35:01
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService{
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RoleMapper roleMapper;
    @Autowired
    private MenuMapper menuMapper;
    @Autowired
    private UserRoleMapper userRoleMapper;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    @Override
    public Result login(LoginDTO loginDTO) {
        User user = getUserByUsername(loginDTO.getUsername());
        // 如果数据库中没有记录或者密码比对错误，提示
        if (user == null || !passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())) {
            throw new MyException(CodeEnum.CODE_401.getCode(), "用户名或密码错误");
        }
        UserDTO userDTO = new UserDTO();
        // 使用hutool工具类拷贝user中的属性
        BeanUtil.copyProperties(user, userDTO);
        // 获取对应角色列表
        List<Role> roles = roleMapper.getRolesByUser(user.getId());
        userDTO.setRoles(roles);
        // 获取对应菜单列表（多个角色）
        if (roles.size() != 0) {
            userDTO.setMenus(menuMapper.getMenusByRoles(roles.stream().map(Role::getId).collect(Collectors.toList())));
        }
        return Result.success(userDTO);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean register(RegisterDTO registerDTO) {
        // 判断确认密码是否一致
        if (!registerDTO.getPassword().equals(registerDTO.getConfirmPwd())) {
            throw new MyException(CodeEnum.CODE_401.getCode(), "两次密码输入不一致");
        }
        // 判断验证码是否正确
        if (!registerDTO.getCode().equals(stringRedisTemplate.opsForValue().get(registerDTO.getEmail()))) {
            throw new MyException(CodeEnum.CODE_403);
        }
        // 判断用户名是否存在
        User user = getUserByUsername(registerDTO.getUsername());
        if (user == null) {
            user = new User();
            BeanUtil.copyProperties(registerDTO, user);
            // 密码加密
            user.setPassword(passwordEncoder.encode(user.getPassword()));
            user.setStatus(StatusEnum.ENABLE.getValue());
            // 添加到用户表
            save(user);
            // 添加到用户角色关联关系表，默认为普通用户
            UserRole userRole = new UserRole(user.getId(), RoleEnum.USER.getId());
            userRoleMapper.insert(userRole);
            return true;
        } else {
            throw new MyException(CodeEnum.CODE_400.getCode(), "用户名已存在");
        }
    }

    @Override
    public Result findPage(Integer pageNum, Integer pageSize, String username) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(User::getId).like(StrUtil.isNotBlank(username), User::getUsername, username);
        // MP多对多查询有问题，一个用户可能有多个角色，在使用MP分页查询时，会将一个用户下的一个角色也作为一条记录，因此不直接使用MP分页查询，而是将sql语句进行拆分，先分页查出用户列表，然后根据用户列表查询对应角色
        // 先分页查询用户列表
        Page<User> page = new Page<>(pageNum, pageSize);
        Page<User> users = userMapper.selectPage(page, wrapper);
        // 获取用户id集合
        List<Integer> ids = users.getRecords().stream().map(User::getId).collect(Collectors.toList());
        // 定义返回map
        Map<String, Object> map = new HashMap<>();
        map.put("total", users.getTotal());
        // 根据用户id集合查询用户信息
        map.put("records", userMapper.selectUserWithRoles(ids));
        return Result.success(map);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveUser(User user) {
        // 获取当前用户身份验证信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        // 如果id为null，表示新增，设置初始密码；否则是修改，设置修改时间
        if (user.getId() == null) {
            // 判断是否有新增权限
            if (!authentication.getAuthorities().contains(new SimpleGrantedAuthority("sys:user:add"))) {
                throw new AccessDeniedException("抱歉，您不具备添加用户的权限");
            }
            // 判断用户是否存在
            if (getUserByUsername(user.getUsername()) == null) {
                // 设置初始密码
                user.setPassword(passwordEncoder.encode(Constants.INIT_PWD));
                // 添加记录
                save(user);
            } else {
                throw new MyException(CodeEnum.CODE_401.getCode(), "用户名已存在");
            }
        } else {
            // 判断是否有修改用户权限
            if (!authentication.getAuthorities().contains(new SimpleGrantedAuthority("sys:user:update"))) {
                throw new AccessDeniedException("抱歉，您不具备修改用户的权限");
            }
            user.setUpdateTime(new Date());
            // 更新记录
            updateById(user);
        }
        return true;
    }

    @Override
    public boolean changePwd(PwdDTO pwdDTO) {
        if (!pwdDTO.getNewPwd().equals(pwdDTO.getConfirmPwd())) {
            throw new MyException(CodeEnum.CODE_401.getCode(), "两次密码输入不一致");
        }
        User user = getUserByUsername(pwdDTO.getUsername());
        if (!passwordEncoder.matches(pwdDTO.getPassword(), user.getPassword())) {
            throw new MyException(CodeEnum.CODE_401.getCode(), "原密码填写错误");
        }
        // 修改密码
        user.setPassword(passwordEncoder.encode(pwdDTO.getNewPwd()));
        user.setUpdateTime(new Date());
        return updateById(user);
    }

    @Value("${file.uploadFolder}")
    private String basePath;

    @Override
    public Result uploadAvatar(MultipartFile file, HttpServletRequest request) {
        try {
            // 返回url
            String url = null;
            // 文件校验
            if (FileUploadUtils.fileVerify(file)) {
                // 获取文件名
                String originalFilename = file.getOriginalFilename();
                // 获取后缀名
                String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
                // 生成uuid作为文件名
                String uuid = FileUploadUtils.getUUID();
                // 分文件夹存储
                String date = DateUtils.dateFormat(new Date());
                String dateName = date + "/" + uuid + suffix;
                System.out.println(dateName);

                // 图片上传路径
                System.out.println(basePath);
                String path = basePath + dateName;
                System.out.println(path);

                // 判断文件夹是否存在
                File finalFile = new File(path);
                if (!finalFile.exists()) {
                    finalFile.mkdirs();
                }
                // 传输文件
                file.transferTo(finalFile);

                // 返回文件访问url
                url = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + "/static/" + dateName;
                System.out.println(url);
            }
            return Result.success("文件上传成功", url);
        } catch (Exception e) {
            e.printStackTrace();
            throw new MyException(e.getMessage());
        }
    }

    /**
     * 根据用户名查找用户信息
     * @author 杨雨盛
     * @date 2023/8/5 8:45
     * @param username
     * @return com.example.terminalservice.pojo.User
     */
    public User getUserByUsername(String username) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername, username);
        User user;
        // 如果查询结果不止一条，则提示系统错误
        try {
            user = userMapper.selectOne(wrapper);
        } catch (Exception e) {
            throw new MyException(CodeEnum.CODE_500);
        }
        return user;
    }
}




